let activeEffect = undefined;

const bucket = new WeakMap();
// 追踪
const track = (target, key) => {
  if (!activeEffect) return;

  let depsMap = bucket.get(target); // WeakMap 由 target --> Map 构成；
  if (!depsMap) {
    bucket.set(target, (depsMap = new Map()));
  }
  let deps = depsMap.get(key); // Map 由 key --> Set 构成
  if (!deps) {
    depsMap.set(key, (deps = new Set()));
  }
  // 最后将当前激活的副作用函数添加到桶中
  deps.add(activeEffect);

  // deps就是一个与当前副作用函数存在关联的依赖集合
  activeEffect.deps.push(deps);
};
// 触发
const trigger = (target, key) => {
  let depsMap = bucket.get(target);
  if (!depsMap) return;
  let deps = depsMap.get(key);
  // 执行副作用函数
  const effectSet = new Set(deps);

  const effectSetRun = new Set();
  effectSet &&
    effectSet.forEach((fn) => {
      // 如果trigger触发的副作用函数与当前正在执行的副作用函数是同一个 则不触发执行
      if (fn !== activeEffect) {
        effectSetRun.add(fn);
      }
    });

  effectSetRun &&
    effectSetRun.forEach((effectFn) => {
      // 如果存在调度器，则直接调用调度器函数 并把当前副作用函数传递过去，由用户自己控制副作用函数的执行(把控制权交给用户)
      if (effectFn.options.scheduler) {
        effectFn.options.scheduler(effectFn);
      } else {
        effectFn();
      }
    });
};

// 增加lazy选项
// 定义一个栈来存储副作用函数
const effectStack = [];
const effect = (fn, options = {}) => {
  const _effectFn = () => {
    cleanup(_effectFn);
    activeEffect = _effectFn;
    // 将当前副作用函数压入栈中
    effectStack.push(_effectFn);
    const res = fn();
    // 副作用函数执行完之后 将其从栈中弹出
    effectStack.pop();
    // activeEffect始终指向栈顶的副作用函数
    activeEffect = effectStack[effectStack.length - 1];
    return res;
  };
  _effectFn.options = options;
  //  用来存储 所有与当前副作用函数相关联的依赖集合
  _effectFn.deps = [];

  // 只有非lazy的时候才执行
  if (!options.lazy) {
    _effectFn();
  }
  return _effectFn;
};

const cleanup = (effectFn) => {
  for (let i = 0; i < effectFn.deps.length; i++) {
    const deps = effectFn.deps[i];
    deps.delete(effectFn);
  }
  effectFn.deps.length = 0;
};

// computed
function computed(getter) {
  console.log("computed===");
  let value;
  let dirty = true;
  let effectFn = effect(getter, {
    lazy: true,
    scheduler() {
      dirty = true;
    },
  });
  let obj = {
    get value() {
      if (dirty) {
        value = effectFn();
        dirty = false;
      }
      return value;
    },
  };
  return obj;
}
// 4.8 计算属性computed与lazy

// 测试代码
const data = {
  foo: 1,
};
const obj = new Proxy(data, {
  get(target, key) {
    track(target, key);
    return target[key];
  },
  set(target, key, val) {
    target[key] = val;
    trigger(target, key);
    return true;
  },
});
// 手动执行副作用函数
// const tt = effect(
//   () => {
//     console.log("执行副作用函数==");
//     return obj.foo + 1;
//   },
//   {
//     lazy: true,
//   }
// );
// setTimeout(() => {
//   console.log("tt====", tt());
// }, 2000);

let obj2 = computed(() => {
  console.log("执行computed==");
  return obj.foo + 1;
});

// setTimeout(() => {
//   console.log("obj2==111==", obj2.value);
// }, 1000);
// setTimeout(() => {
//   obj.foo += 1;
//   console.log("obj2==222==", obj2.value);
// }, 2000);
effect(() => {
  // ???当obj.foo发生变化时，obj2.value不会重新执行，这是一个缺陷。后续在看
  console.log("effect==", obj2.value);
});
obj.foo += 1;
console.log("obj2==", obj2.value);
